package com.nebula.salary.portal.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.nebula.salary.common.constants.Const;
import com.nebula.salary.common.utils.JwtTokenUtil;
import com.nebula.salary.common.utils.RedisUtil;
import com.nebula.salary.common.utils.SecurityUtil;
import com.nebula.salary.model.pojo.Navigation;
import com.nebula.salary.model.pojo.RoleNavigation;
import com.nebula.salary.model.pojo.User;
import com.nebula.salary.model.vo.LoginUser;
import com.nebula.salary.model.vo.RoleMenuVo;
import com.nebula.salary.portal.mapper.NavigationMapper;
import com.nebula.salary.portal.mapper.RoleNavigationMapper;
import com.nebula.salary.portal.service.INavigationService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author codex
 * @since 2022-01-24
 */
@Service
public class NavigationServiceImpl extends ServiceImpl<NavigationMapper, Navigation> implements INavigationService {
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    private RedisUtil redisUtil;
    
    @Autowired
    private NavigationMapper navigationMapper;
    
    @Autowired
    private RoleNavigationMapper roleNavigationMapper;
    
    /**
     * 获取登录用户有权限的菜单目录
     *
     * @param request request
     * @return 菜单
     */
    @Override
    public List<Navigation> getNavigation(HttpServletRequest request) {
        LoginUser loginUser = jwtTokenUtil.getLoginUser(request);
        
        String key = Const.MENU_ROLE_KEY + SecurityUtil.getUsername() + "_" + SecurityUtil.getUserId();
        
        List<Navigation> navigations = redisUtil.getObject(key);
        
        if (navigations == null || navigations.size() <= 0) {
            navigations = assembleNavigationVo(loginUser.getUser().getUserId());
            
            // 将菜单存到redis缓存中
            redisUtil.setObject(key, navigations, 5, TimeUnit.MINUTES);
        }
        
        return navigations;
    }
    
    
    /**
     * 根据角色获取菜单列表
     *
     * @return 菜单集合
     */
    @Override
    public List<Navigation> getNavigationWithRole() {
        return navigationMapper.getNavigationWithRole();
    }
    
    /**
     * 根据角色ID获取选中的菜单编号集合
     *
     * @param roleId 角色ID
     * @return 菜单编号集合
     */
    @Override
    public List<Integer> selectMenuListByRoleId(Integer roleId) {
        return baseMapper.selectMenuListByRoleId(roleId);
    }
    
    /**
     * 根据用户ID获取选中的菜单编号集合
     *
     * @return 菜单集合
     */
    @Override
    public List<Navigation> selectMenuListByUserId() {
        return this.treeSelect();
    }
    
    /**
     * 构建前端角色管理所需要下拉树结构
     *
     * @param navigations 菜单列表
     * @return 下拉树结构列表
     */
    @Override
    public List<RoleMenuVo> buildMenuTreeSelect(List<Navigation> navigations) {
        List<Navigation> list = buildMenuTree(navigations);
        return list.stream().map(RoleMenuVo::new).collect(Collectors.toList());
    }
    
    /**
     * 构建前端所需要下拉树结构
     *
     * @param navigations 菜单列表
     * @return 下拉树结构列表
     */
    @Override
	public List<Navigation> buildTreeList(List<Navigation> navigations) {
    	List<Navigation> list = buildMenuTree(navigations);
		return list;
	}


	/**
     * 根据用户ID获取菜单树形列表
     *
     * @return
     */
    @Override
    public List<Navigation> treeSelect() {
        List<Navigation> list = null;
        Long userId = SecurityUtil.getUserId();
        if (User.isAdmin(userId)) {
            // 超级管理员，查询所有
            list = baseMapper.selectList(new QueryWrapper<Navigation>().orderByAsc(Navigation.NAV_ID).orderByAsc(Navigation.SORT));
        } else {
            list = baseMapper.selectMenuListByUserId(userId);
        }
        return list;
    }
    
    /**
	 * 校验菜单名称是否唯一
	 * 
	 * @param navigation 菜单信息
	 * @return 校验结果
	 */
    @Override
	public String checkItemUnique(Navigation navigation) {
		Integer id = navigation.getNavId() == null ? -1 : navigation.getNavId();
		Navigation info = baseMapper.selectOne(new QueryWrapper<Navigation>().eq(Navigation.ITEM, navigation.getItem()));
		if (info != null && info.getNavId().intValue() != id.intValue()) {
			return Const.NOT_UNIQUE;
		}
		return Const.UNIQUE;
	}


    /**
	 * 校验显示排序是否唯一
	 * 
	 * @param navigation 菜单信息
	 * @return 校验结果
	 */
	@Override
	public String checkSortUnique(Navigation navigation) {
		Integer id = navigation.getNavId() == null ? -1 : navigation.getNavId();
		Navigation info = baseMapper.selectOne(new QueryWrapper<Navigation>().eq(Navigation.SORT, navigation.getSort()));
		if (info != null && info.getNavId().intValue() != id.intValue()) {
			return Const.NOT_UNIQUE;
		}
		return Const.UNIQUE;
	}


	/**
	 * 校验路由地址是否唯一
	 * 
	 * @param navigation 菜单信息
	 * @return校验结果
	 */
	@Override
	public String checkPathUnique(Navigation navigation) {
		Integer id = navigation.getNavId() == null ? -1 : navigation.getNavId();
		Navigation info = baseMapper.selectOne(new QueryWrapper<Navigation>().eq(Navigation.PATH, navigation.getPath()));
		if (info != null && info.getNavId().intValue() != id.intValue()) {
			return Const.NOT_UNIQUE;
		}
		return Const.UNIQUE;
	}

	/**
	 * 新增菜单
	 * 
	 * @param navigation 菜单信息
	 * @return 影响行数
	 */
    @Override
    @Transactional(rollbackFor = Exception.class)
	public int add(Navigation navigation) {

    	// 一级目录
    	if (navigation.getParentId().equals(0)) {
			navigation.setComponent("Layout");
			navigation.setUrl("/");
		}
    	
    	// 新增菜单
    	baseMapper.insert(navigation);
    	
    	// 删除缓存中的权限数据
    	redisUtil.deleteObject(Const.MENU_ROLE_KEY + Const.ROLE_SECURITY_PREFIX);
    	redisUtil.deleteObject(Const.MENU_ROLE_KEY + SecurityUtil.getUsername() + "_" + SecurityUtil.getUserId());
    	// 将菜单加入超级管理员角色中
    	RoleNavigation roleNavigation = new RoleNavigation();
    	roleNavigation.setNavId(navigation.getNavId());
    	roleNavigation.setRoleId(Const.HOST_ROLE_ID);
    	
		return roleNavigationMapper.insert(roleNavigation);
	}
    
    /**
     * 修改菜单
     *
     * @param navigation 菜单信息
     * @return 影响行数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(Navigation navigation) {
    
        // 删除缓存中的权限数据
        redisUtil.deleteObject(Const.MENU_ROLE_KEY + Const.ROLE_SECURITY_PREFIX);
        redisUtil.deleteObject(Const.MENU_ROLE_KEY + SecurityUtil.getUsername() + "_" + SecurityUtil.getUserId());
        
        return baseMapper.updateById(navigation);
    }

    /**
	 * 校验菜单是否有子菜单
	 * 
	 * @param navId 菜单编号
	 * @return 结果
	 */
	@Override
	public boolean hasChildByNavId(Integer navId) {
		return baseMapper.selectCount(new QueryWrapper<Navigation>().eq(Navigation.PARENT_ID, navId)) > 0;
	}

	/**
	 * 校验菜单是否已分配
	 * 
	 * @param navId 菜单编号
	 * @return 结果
	 */
	@Override
	public boolean checkMenuExistRole(Integer navId) {
		QueryWrapper<RoleNavigation> query = new QueryWrapper<RoleNavigation>();
		query.eq(RoleNavigation.NAV_ID, navId).ne(RoleNavigation.ROLE_ID, Const.HOST_ROLE_ID);
		return roleNavigationMapper.selectCount(query) > 0;
	}
    
    /**
     * 构建前端所需要树结构
     *
     * @param navigations 菜单列表
     * @return 树结构列表
     */
    private List<Navigation> buildMenuTree(List<Navigation> navigations) {
        List<Navigation> returnList = new ArrayList<>();
        List<Integer> tempList = new ArrayList<>();
        for (Navigation navigation : navigations) {
            tempList.add(navigation.getNavId());
        }
        
        for (Navigation next : navigations) {
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(next.getParentId())) {
                recursionFn(navigations, next);
                returnList.add(next);
            }
        }
        if (returnList.isEmpty()) {
            returnList = navigations;
        }
        return returnList;
    }
    
    /**
     * 递归列表
     *
     * @param navigations 菜单列表
     * @param navigation  菜单
     */
    private void recursionFn(List<Navigation> navigations, Navigation navigation) {
        // 得到子节点列表
        List<Navigation> childList = getChildList(navigations, navigation);
        navigation.setChildren(childList);
        for (Navigation tNavigation : childList) {
            if (hasChild(navigations, tNavigation)) {
                recursionFn(navigations, tNavigation);
            }
        }
    }
    
    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<Navigation> navigations, Navigation tNavigation) {
        return getChildList(navigations, tNavigation).size() > 0;
    }
    
    /**
     * 得到子节点列表
     */
    private List<Navigation> getChildList(List<Navigation> navigations, Navigation navigation) {
        List<Navigation> tList = new ArrayList<>();
        for (Navigation n : navigations) {
            if (n.getParentId().intValue() == navigation.getNavId().intValue()) {
                tList.add(n);
            }
        }
        return tList;
    }
    
    /**
     * 组装菜单集合
     *
     * @param userId 用户ID
     * @return 有父子关系的菜单集合
     */
    private List<Navigation> assembleNavigationVo(Long userId) {
        List<Navigation> navigations = baseMapper.selectByParentIdAndUserId(0, userId);
        for (Navigation navigation : navigations) {
            List<Navigation> children = baseMapper.selectByParentIdAndUserId(navigation.getNavId(), userId);
            if (children != null && children.size() > 0) {
                navigation.setChildren(children);
            }
        }
        
        return navigations;
    }
}
